home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Symantec Visual Cafe for Java 2.5
/
symantec-visual-cafe-2.5-database-dev-edition.iso
/
Visual Cafe Pro v1.0
/
SOURCE.BIN
/
SplitterPanel.java
< prev
next >
Encoding:
Amiga (detected)
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
Macintosh to JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Java Source
|
1997-06-19
|
54.9 KB
|
1,937 lines
package symantec.itools.awt;
import java.awt.Panel;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Point;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.LayoutManager;
import java.awt.Graphics;
import java.awt.Container;
import java.awt.Event;
/**
* Creates a container that can be divided into a number of subpanels which
* holds visual components and other panels, and specifically to:
* <UL>
* <DT>╖ create a subcontainer that organizes container space within an Applet,
* Frame or Dialog container. This simplifies your component layout task.</DT>
* <DT>╖ hold other specialized Panel containers.</DT>
* </UL>
* The SplitterPanel component is the parent Panel containing a set of
* subpanels. You must write code to divide the parent panel into subpanels.
* You must set panel defaults in your project source code, but can drop
* components into subpanels from the Palette.
* <p>
* SplitterPanel automatically recognizes the MouseDrag event as the command
* to resize the appropriate subpanel boundary.
* <p>
* Subpanels can be resized at runtime by dragging panel borders with the mouse.
* <p>
* This code example splits a SplitterPanel into four subpanels and adds a
* button to two of the subpanels.
* <UL><pre>
* splitterPanel1.split(splitterPanel1.SPLIT_HOR);
* splitterPanel1.getTopPanel().split(splitterPanel1.SPLIT_VER);
* splitterPanel1.getBottomPanel().split(splitterPanel1.SPLIT_VER);
* // add a button to the top left panel
* splitterPanel1.getTopLeftPanel().add(new Button("Top Left"));
* // add a button to the lower right panel
* splitterPanel1.getBottomRightPanel().add(new Button("Bottom Right"));
* </pre></UL>
* Individual subpanels can be accessed in code using any of these methods:
* getBottomLeftPanel,
* getBottomPanel,
* getBottomRightPanel,
* getLeftPanel,
* getRightPanel,
* getSub2Panel,
* getSubPanel,
* getTopLeftPanel,
* getTopPanel,
* getTopRightPanel.
* <p>
* @version 1.0, Nov 26, 1996
*
* @author Symantec
*
*/
public class SplitterPanel
extends Panel
{
//--------------------------------------------------
// constants
//--------------------------------------------------
/**
* Vertical panel split definition. The panel contains/will
* contain two subpanels, left and right.
* @see #split
*/
public static final int SPLIT_VER = 1;
/**
* Horizontal panel split definition. The panel contains/will
* contain two subpanels, top and bottom.
* @see #split
*/
public static final int SPLIT_HOR = 2;
/**
* Vertical pane split constant for the SplitterPanel constructor.
* This splits the pane into two vertical panes.
* @see #SplitterPanel
*/
public static final int SPLIT_VERTICAL = 1;
/**
* Horizontal pane split constant for the SplitterPanel constructor.
* This splits the pane into two horizontal panes.
* @see #SplitterPanel
*/
public static final int SPLIT_HORIZONTAL = 2;
/**
* Vertical/Horizontal pane split constant for the SplitterPanel constructor.
* This splits the pane into four panes.
* @see #SplitterPanel
*/
public static final int SPLIT_BOTH = 3;
/**
* Vertical/Horizontal pane split constant for the SplitterPanel constructor.
* This splits the pane into four panes.
* @see #SplitterPanel
*/
public static final int SPLIT_BOTH_V = 4;
/**
* Vertical/Horizontal pane split constant for the SplitterPanel constructor.
* This splits the pane into three panes, with a single larger pane on the
* right.
* @see #SplitterPanel
*/
public static final int SPLIT3_LEFT = 5;
/**
* Vertical/Horizontal pane split constant for the SplitterPanel constructor.
* This splits the pane into three panes, with a single larger pane on the
* left.
* @see #SplitterPanel
*/
public static final int SPLIT3_RIGHT = 6;
/**
* Vertical/Horizontal pane split constant for the SplitterPanel constructor.
* This splits the pane into three panes, with a single larger pane on the
* bottom.
* @see #SplitterPanel
*/
public static final int SPLIT3_TOP = 7;
/**
* Vertical/Horizontal pane split constant for the SplitterPanel constructor.
* This splits the pane into three panes, with a single larger pane on the
* top.
* @see #SplitterPanel
*/
public static final int SPLIT3_BOTTOM = 8;
private static final int SPLIT_NONE = 0; //Panel contains no subpanels, initial state
//--------------------------------------------------
// class variables
//--------------------------------------------------
/**
* This flag specifies how to draw border around panes.
* Must be used in conjunction with setBdrSizes or other setXXX
* calls to take effect.
*/
public boolean use3dBdr = true;
private boolean enforceMinDim = false;
private boolean propResize = true;
/**
* Determines the cursor to use when drag the split between panes.
* Use Frame.HAND_CURSOR, etc to choose a cursor or
* set to Integer.MIN_VALUE to prevent cursor change.
* @see java.awt.Frame
*/
public int moveSplitCursor = Frame.MOVE_CURSOR;
private int splitType;
private boolean isOuter;
private Dimension curDim;
private Point curLoc;
private boolean dimChanged;
private boolean heightOnly;
private boolean widthOnly;
private boolean doMoveSplit = false;
private boolean cursorChanged = false;
private int moveFromX;
private int moveFromY;
private Component spComponent;
private SplitterPanel sub1;
private SplitterPanel sub2;
private SplitterPanel innerSP;
private SplitterPanel outerSP;
static private SplitterPanel cursorCh = null;
/**
* Dimension variables.
* Note that these should be set explicitly if you wish to override
* the built in calculation of these dimensions. For instance, do
* not set (or set to zeros), the value for prefDim if you want the
* SplitterPanel to calculate this as the sum of the subcomponents
* prefDims. minDim is used when the user explicitly sets the
* minimum size; prefDim is used when the user explicitly sets the
* preferred size.
*/
private Dimension minDim;
private Dimension prefDim;
private Color gapColor;
private int iGapWidth;
private int iGapHeight;
private int oGapWidth;
private int oGapHeight;
private int iBdrSize;
private int oBdrSize;
private boolean bOsFlag;
//--------------------------------------------------
// constructors
//--------------------------------------------------
/**
* Constructs a new default SplitterPanel.
*/
public SplitterPanel()
{
this(true);
}
/**
* Constructs a new SplitterPanel of the specified size which
* is ready to be split into panes.
* @param spWidth value to be used for the initial width of
* the SplitterPanel
* @param spHeight value to be used for the initial height of
* the SplitterPanel
*/
public SplitterPanel(int spWidth, int spHeight)
{
this(true);
if (this == null)
return;
resize(spWidth, spHeight);
}
/**
* Constructs a new SplitterPanel of specified size which is
* split into panes.
* @param spWidth value to be used for the initial width of
* the SplitterPanel
* @param spHeight value to be used for the initial height
* of the SplitterPanel
* @param splitSpec specifies the type of split
* @see #SPLIT_VERTICAL
* @see #SPLIT_HORIZONTAL
* @see #SPLIT_BOTH
* @see #SPLIT_BOTH_V
* @see #SPLIT3_LEFT
* @see #SPLIT3_RIGHT
* @see #SPLIT3_TOP
* @see #SPLIT3_BOTTOM
*/
public SplitterPanel(int spWidth, int spHeight, int splitSpec)
{
this(splitSpec);
if (this == null)
return;
resize(spWidth, spHeight);
}
/**
* Constructs a new SplitterPanel which is split into panes.
* @param splitSpec specifies the type of split
* @see #SPLIT_VERTICAL
* @see #SPLIT_HORIZONTAL
* @see #SPLIT_BOTH
* @see #SPLIT_BOTH_V
* @see #SPLIT3_LEFT
* @see #SPLIT3_RIGHT
* @see #SPLIT3_TOP
* @see #SPLIT3_BOTTOM
*/
public SplitterPanel(int splitSpec)
{
this(splitSpec, null, null, null, null);
}
/**
* Constructs a new SplitterPanel which is split into panes
* to which components are added.
* @param splitSpec specifies the type of split
* @see #SPLIT_VERTICAL
* @see #SPLIT_HORIZONTAL
* @see #SPLIT_BOTH
* @see #SPLIT_BOTH_V
* @see #SPLIT3_LEFT
* @see #SPLIT3_RIGHT
* @see #SPLIT3_TOP
* @see #SPLIT3_BOTTOM
* @param compR1C1 specifies the component in row 1, column 1
* @param compR1C2 specifies the component in row 1, column 2
* @param compR2C1 specifies the component in row 2, column 1
* @param compR2C2 specifies the component in row 2, column 2
*/
public SplitterPanel(int splitSpec, Component compR1C1, Component compR1C2, Component compR2C1, Component compR2C2)
{
this(true);
if (this == null)
return;
switch (splitSpec)
{
case SPLIT_NONE:
{
if (compR1C1 != null) this.add(compR1C1);
break;
}
case SPLIT_VERTICAL:
{
split(SPLIT_VER, compR1C1, compR1C2);
break;
}
case SPLIT_HORIZONTAL:
{
split(SPLIT_HOR, compR1C1, compR2C1);
break;
}
case SPLIT_BOTH:
{
split(SPLIT_HOR);
getSubPanel(1).split(SPLIT_VER, compR1C1, compR1C2);
getSubPanel(2).split(SPLIT_VER, compR2C1, compR2C2);
break;
}
case SPLIT_BOTH_V:
{
split(SPLIT_VER);
getSubPanel(1).split(SPLIT_HOR, compR1C1, compR2C1);
getSubPanel(2).split(SPLIT_HOR, compR1C2, compR2C2);
break;
}
case SPLIT3_LEFT:
{
split(SPLIT_VER, null, compR1C2);
getSubPanel(1).split(SPLIT_HOR, compR1C1, compR2C1);
break;
}
case SPLIT3_RIGHT:
{
split(SPLIT_VER, compR1C1, null);
getSubPanel(2).split(SPLIT_HOR, compR1C2, compR2C2);
break;
}
case SPLIT3_TOP:
{
split(SPLIT_HOR, null, compR2C1);
getSubPanel(1).split(SPLIT_VER, compR1C1, compR1C2);
break;
}
case SPLIT3_BOTTOM:
{
split(SPLIT_HOR, compR1C1, null);
getSubPanel(2).split(SPLIT_VER, compR2C1, compR2C2);
break;
}
}
}
private SplitterPanel(boolean isOuter)
{
super();
this.isOuter = isOuter;
dimChanged = true;
heightOnly = false;
widthOnly = false;
splitType = SPLIT_NONE;
setLayout(null);
// Set defaults
minDim = new Dimension(0, 0);
curDim = new Dimension(0, 0);
curLoc = new Point(0, 0);
prefDim = new Dimension(0, 0);
gapColor = Color.lightGray;
iGapWidth = 3;
iGapHeight = 3;
iBdrSize = 2;
oGapWidth = 3;
oGapHeight = 3;
oBdrSize = 2;
//If outer, then create the REQUIRED first inner Panel
if (isOuter)
{
innerSP = new SplitterPanel(false);
super.add(innerSP, -1);
outerSP = this;
innerSP.outerSP = this;
}
}
//--------------------------------------------------
// accessor methods
//--------------------------------------------------
/**
* Sets the color of the gap between panes and around the outside
* border.
* @param c the color to use
* @see #getGapColor
*/
public void setGapColor(Color c)
{
this.gapColor = new Color(c.getRed(), c.getGreen(), c.getBlue());
propagateChanges();
}
/**
* Gets the color of the gap between panes and around
* the outside.
* @return current gap color
* @see #setGapColor
*/
public Color getGapColor()
{
return gapColor;
}
private void adjustOGapBdr()
{
if (this.oGapWidth < this.oBdrSize)
this.oGapWidth = Math.max(0, this.oBdrSize);
if (this.oGapHeight < this.oBdrSize)
this.oGapHeight = Math.max(0, this.oBdrSize);
}
/**
* Sets the size of the gap between panes and around the
* outside.
* @param gapSize the size in pixels
*/
public void setGapSizes(int gapSize)
{
this.iGapWidth = gapSize;
this.iGapHeight = gapSize;
this.oGapWidth = gapSize;
this.oGapHeight = gapSize;
adjustOGapBdr();
propagateChanges();
invalidate();
}
/**
* Sets the size of the gap between panes and around the
* outside.
* @param gapWidth the size in pixels
* @param gapHeight the size in pixels
*/
public void setGapSizes(int gapWidth, int gapHeight)
{
this.iGapWidth = gapWidth;
this.iGapHeight = gapHeight;
this.oGapWidth = gapWidth;
this.oGapHeight = gapHeight;
adjustOGapBdr();
propagateChanges();
invalidate();
}
/**
* Sets the size of the gap between panes and around the
* outside.
* @param iGapWidth the size in pixels between panes
* @param iGapHeight the size in pixels between panes
* @param oGapWidth the size in pixels around the outside
* @param oGapHeight the size in pixels around the outside
*/
public void setGapSizes(int iGapWidth, int iGapHeight, int oGapWidth, int oGapHeight)
{
this.iGapWidth = iGapWidth;
this.iGapHeight = iGapHeight;
this.oGapWidth = oGapWidth;
this.oGapHeight = oGapHeight;
adjustOGapBdr();
propagateChanges();
invalidate();
}
/**
* Sets the size of the gap between panes and around the
* outside.
* @param iBdrSize the size in pixels between panes
* @param oBdrSize the size in pixels around the outside
*/
public void setBdrSizes(int iBdrSize, int oBdrSize)
{
this.iBdrSize = iBdrSize;
this.oBdrSize = oBdrSize;
adjustOGapBdr();
propagateChanges();
invalidate();
}
/**
* Sets the size of the border between panes and around the
* outside.
* @param bdrSize the size in pixels
*/
public void setBdrSizes(int bdrSize)
{
this.iBdrSize = bdrSize;
this.oBdrSize = bdrSize;
adjustOGapBdr();
propagateChanges();
invalidate();
}
/**
* Gets the color of the gap between panes and around
* the outside.
* @return the current gap color
* @see setGapColor
*/
public Color gapColor()
{
return this.gapColor;
}
/**
* Sets the "enforce minimum dimension" mode.
* @param theFlag if true, prevents dragging the split between panes
* from making a panel smaller than the minimum size of its component.
* @see #getEnforceMinDim
*/
public void setEnforceMinDim(boolean theFlag)
{
enforceMinDim = theFlag;
propagateChanges();
}
/**
* Gets the current "enforce minimum dimension" mode.
* @return the current mode
* @see #setEnforceMinDim
*/
public boolean getEnforceMinDim()
{
return enforceMinDim;
}
/**
* Sets the "resize propagation" mode.
* Set this to false to prevent the automatic resizing of SplitterPanel
* panes from also calling resize() for the component that you add.
* The default behavior propagates the resizing effect of dragging the
* split between panes by calling move and resize for the panels that have
* been added to those panes. Clearing this introduces a clipping effect,
* rather than a scaling effect.
* @param theFlag if true, resize propagation is enabled; if false,
* resize propagation is disabled
* @see #getPropResize
*/
public void setPropResize(boolean theFlag)
{
//Controls whether reshapes are propagated into added components
propResize = theFlag;
propagateChanges();
}
/**
* Gets the current "resize propagation" mode.
* @return the current "resize propagation" mode
* @see #setPropResize
*/
public boolean getPropResize()
{
return propResize;
}
/**
* Gets the type of split for this SplitterPanel.
* @return splitType SPLIT_VER or SPLIT_HOR
* @see #SPLIT_VER
* @see #SPLIT_HOR
*/
public int getSplitType()
{
SplitterPanel targetSP = this;
if (isOuter) targetSP = this.innerSP;
return targetSP.splitType;
}
/**
* Gets a pane within a SplitterPanel.
* @param n pane number (1 for first pane, or 2 for second
* pane)
* @return the pane, or null if none
*/
public SplitterPanel getSubPanel(int n)
{
SplitterPanel targetSP = this;
if (isOuter)
targetSP = this.innerSP;
if (n == 1)
return targetSP.sub1;
if (n == 2)
return targetSP.sub2;
return null;
}
/**
* Gets a pane within a SplitterPanel of specified type.
* @param n pane number (1 for first pane, or 2 for second
* pane)
* @param theSplitType specifies the type of split desired
* (SPLIT_VER or SPLIT_HOR)
* @return the pane, or null if none of specified
* type
* @see #SPLIT_VER
* @see #SPLIT_HOR
*/
public SplitterPanel getSubPanel(int n, int theSplitType)
{
if (getSplitType() == theSplitType)
return getSubPanel(n);
return null;
}
/**
* Gets a pane within a doubly split SplitterPanel with
* specified split types.
* @param n1 pane number (1 for first pane, or 2 for second pane)
* @param theSplitType1 specifies the type of split desired
* (SPLIT_VER or SPLIT_HOR)
* @param n2 pane number (1 for first pane, or 2 for second pane)
* @param theSplitType2 specifies the type of split desired
* (SPLIT_VER or SPLIT_HOR)
* @return the pane, or null if none of specified
* type
* @see #SPLIT_VER
* @see #SPLIT_HOR
*/
public SplitterPanel getSub2Panel(int n1, int theSplitType1, int n2, int theSplitType2)
{
SplitterPanel theSP;
theSP = getSubPanel(n1, theSplitType1);
if (theSP != null)
return theSP.getSubPanel(n2, theSplitType2);
return null;
}
/**
* Gets top panel of a horizontally split SplitterPanel.
* @return the pane, or null if none of specified
* type
*/
public SplitterPanel getTopPanel()
{
return getSubPanel(1, SPLIT_HOR);
}
/**
* Gets the bottom panel.
* @return the pane, or null if none of specified
* type
*/
public SplitterPanel getBottomPanel()
{
return getSubPanel(2, SPLIT_HOR);
}
/**
* Gets the left panel.
* @return the pane, or null if none of specified
* type
*/
public SplitterPanel getLeftPanel()
{
return getSubPanel(1, SPLIT_VER);
}
/**
* Gets the right panel.
* @return the pane, or null if none of specified
* type
*/
public SplitterPanel getRightPanel()
{
return getSubPanel(2, SPLIT_VER);
}
/**
* Gets the top left panel.
* @return the pane, or null if none of specified
* type
*/
public SplitterPanel getTopLeftPanel()
{
if (getSplitType() == SPLIT_HOR)
return getSub2Panel(1, SPLIT_HOR, 1, SPLIT_VER);
return getSub2Panel(1, SPLIT_VER, 1, SPLIT_HOR);
}
/**
* Gets the top right panel.
* @return the pane, or null if none of specified
* type
*/
public SplitterPanel getTopRightPanel()
{
if (getSplitType() == SPLIT_HOR)
return getSub2Panel(1, SPLIT_HOR, 2, SPLIT_VER);
return getSub2Panel(2, SPLIT_VER, 1, SPLIT_HOR);
}
/**
* Gets the bottom left panel.
* @return the pane, or null if none of specified
* type
*/
public SplitterPanel getBottomLeftPanel()
{
if (getSplitType() == SPLIT_HOR)
return getSub2Panel(2, SPLIT_HOR, 1, SPLIT_VER);
return getSub2Panel(1, SPLIT_VER, 2, SPLIT_HOR);
}
/**
* Gets the bottom right panel.
* @return the pane, or null if none of specified
* type
*/
public SplitterPanel getBottomRightPanel()
{
if (getSplitType() == SPLIT_HOR)
return getSub2Panel(2, SPLIT_HOR, 2, SPLIT_VER);
return getSub2Panel(2, SPLIT_VER, 2, SPLIT_HOR);
}
//--------------------------------------------------
// event methods
//--------------------------------------------------
/**
* This standard routine is called by the Java AWT to handle events.
* Do NOT override this method.
* @param evt the event to handle
* @return true if the event was handled an no further action is needed,
* false to pass the event to this component's parent
*/
public boolean handleEvent(Event evt)
{
if ((evt.target == this) && isOuter)
{
return oGapThis(evt);
}
switch (evt.id)
{
case Event.MOUSE_MOVE:
{
if ((evt.target == this) && (splitType == SPLIT_HOR) && inGap1(evt.x, evt.y))
{
setCursor(moveSplitCursor);
return true;
}
else if ((evt.target == this) && (splitType == SPLIT_VER) && inGap1(evt.x, evt.y))
{
setCursor(moveSplitCursor);
return true;
}
else
{
//if (!doMoveSplit && !inGap(evt)) resetCursor();
return true;
}
}
case Event.MOUSE_DOWN:
{
if ((evt.target == this) && (splitType == SPLIT_HOR) && outerSP.cursorChanged)
{
doMoveSplit = true;
moveFromX = evt.x;
moveFromY = evt.y;
return true;
}
else if ((evt.target == this) && (splitType == SPLIT_VER) && outerSP.cursorChanged)
{
doMoveSplit = true;
moveFromX = evt.x;
moveFromY = evt.y;
return true;
}
if (outerSP.cursorChanged)
{
Point p = location();
getParent().postEvent(new Event(cursorCh, evt.when, evt.id, p.x + evt.x, p.y + evt.y, evt.key, evt.modifiers, evt.arg));
return true;
}
}
case Event.MOUSE_DRAG:
{
if (doMoveSplit == true)
{
setCursor(moveSplitCursor);
return true;
}
}
case Event.MOUSE_UP:
{
//Important -- Do NOT check that this is the target for mouse up
if (doMoveSplit == true)
{
moveSplit(evt.x - moveFromX, evt.y - moveFromY);
doMoveSplit = false;
return true;
}
}
}
return super.handleEvent(evt);
}
//--------------------------------------------------
// class methods
//--------------------------------------------------
//--------------------------------------------------
// member methods
//--------------------------------------------------
/**
* Gets the current inside gap width.
* @return size in pixels between panes
*/
public int iGapWidth()
{
return this.iGapWidth;
}
/**
* Gets the current inside gap height.
* @return size in pixels between panes
*/
public int iGapHeight()
{
return this.iGapHeight;
}
/**
* Gets the current inside gap width.
* @return size in pixels around
*/
public int oGapWidth()
{
return this.oGapWidth;
}
/**
* Gets the current outside gap height.
* @return size in pixels around outside
*/
public int oGapHeight()
{
return this.oGapHeight;
}
/**
* Gets the current pane border size.
* @return size in pixels around panes
*/
public int iBdrSize()
{
return this.iBdrSize;
}
/**
* Gets the current outside border size.
* @return size in pixels around outside
*/
public int oBdrSize()
{
return this.oBdrSize;
}
private int optSize(int total, int _gap, int _min1, int _min2, int _pref1, int _pref2)
{
int diff;
float scale;
int gap;
int min1;
int min2;
int pref1;
int pref2;
gap = Math.max(_gap, 0);
min1 = Math.max(_min1, 1);
min2 = Math.max(_min2, 1);
pref1 = Math.max(_pref1, min1);
pref2 = Math.max(_pref2, min2);
diff = total - (pref1 + pref2 + gap);
if (diff >= 0)
{
scale = (float)pref1/(float)(pref1 + pref2);
return pref1 + (int)(diff*scale);
}
diff = total - (min1 + min2 + gap);
if (diff >= 0)
{
scale = (float)pref1/(float)(pref1 + pref2);
return min1 + (int)(diff*scale);
}
if (total > gap)
{
scale = (float)min1/(float)(min1 + min2);
return min1 + (int)(diff*scale);
}
return total;
}
/**
* This standard routine is called by the Java AWT to handle the laying
* out of components within other components.
*/
public synchronized void layout() {
placeComponents();
}
private void placeComponents()
{
Dimension prefDim1;
Dimension prefDim2;
Dimension minDim1;
Dimension minDim2;
int sub1X;
int sub1Y;
int sub2X;
int sub2Y;
int sub1Width;
int sub1Height;
int sub2Width;
int sub2Height;
Rectangle rect = new Rectangle(curLoc.x, curLoc.y, curDim.width, curDim.height);
if (isOuter)
{
sub1X = oGapWidth + oBdrSize;
sub1Y = oGapHeight + oBdrSize;
sub1Width = rect.width - ((2 * oGapWidth + oBdrSize +1 ) );
sub1Height = rect.height - ((2 * oGapHeight + oBdrSize + 1) );
innerSP.reshape(sub1X, sub1Y, sub1Width, sub1Height);
}
else
{
if (sub1 == null)
{
minDim1 = new Dimension(1, 1);
prefDim1 = new Dimension(1, 1);
}
else
{
minDim1 = sub1.minimumSize();
prefDim1 = sub1.preferredSize();
}
if (sub2 == null)
{
minDim2 = new Dimension(1, 1);
prefDim2 = new Dimension(1, 1);
}
else
{
minDim2 = sub2.minimumSize();
prefDim2 = sub2.preferredSize();
}
dimChanged = false;
switch (splitType)
{
case SPLIT_NONE:
{
if (spComponent != null)
{
sub1X = iBdrSize;
sub1Y = iBdrSize;
sub1Width = rect.width - (2 * iBdrSize);
sub1Height = rect.height - (2 * iBdrSize);
if (sub1Width < 0)
sub1Width = 0;
if (sub1Height < 0)
sub1Height = 0;
if (propResize) spComponent.reshape(sub1X, sub1Y, sub1Width, sub1Height);
else spComponent.move(sub1X, sub1Y);
spComponent.layout();
spComponent.invalidate();
spComponent.validate();
try {
spComponent.repaint();
} catch(Exception e) {
repaint();
}
}
return;
}
case SPLIT_VER:
{
sub1X = 0;
sub1Y = 0;
sub1Width = optSize(rect.width, iGapWidth, minDim1.width, minDim2.width, prefDim1.width, prefDim2.width);
sub1Height = rect.height;
sub2X = sub1Width + iGapWidth;
sub2Y = 0;
sub2Width = rect.width - sub2X;
sub2Height = rect.height;
break;
}
case SPLIT_HOR:
{
sub1X = 0;
sub1Y = 0;
sub1Width = rect.width;
sub1Height = optSize(rect.height, iGapHeight, minDim1.height, minDim2.height, prefDim1.height, prefDim2.height);
sub2X = 0;
sub2Y = sub1Height + iGapHeight;
sub2Width = rect.width;
sub2Height = rect.height - sub2Y;
break;
}
default:
return;
}
if (heightOnly)
{
heightOnly = false;
sub1.reshapeHeight(sub1Y, sub1Height);
sub2.reshapeHeight(sub2Y, sub2Height);
}
else if (widthOnly)
{
widthOnly = false;
sub1.reshapeWidth(sub1X, sub1Width);
sub2.reshapeWidth(sub2X, sub2Width);
}
else
{
sub1.reshape(sub1X, sub1Y, sub1Width, sub1Height);
sub2.reshape(sub2X, sub2Y, sub2Width, sub2Height);
}
sub1.invalidate();
sub1.validate();
sub1.repaint();
sub2.invalidate();
sub2.validate();
sub2.repaint();
}
}
/**
* Moves the location of the gap between panes.
* @param spX specifies relative movement in pixels along X axis
* @param spY specifies relative movement in pixels along Y axis
*/
public void moveSplit(int spX, int spY)
{
Rectangle cur1;
Rectangle cur2;
Dimension min1;
Dimension min2;
int delta;
int newX1;
int newY1;
int newWidth1;
int newHeight1;
int newX2;
int newY2;
int newWidth2;
int newHeight2;
if (isOuter)
{
innerSP.moveSplit(spX, spY);
return;
}
else
{
min1 = sub1.minimumSize();
min2 = sub2.minimumSize();
cur1 = new Rectangle(sub1.curLoc.x, sub1.curLoc.y, sub1.curDim.width, sub1.curDim.height);
cur2 = new Rectangle(sub2.curLoc.x, sub2.curLoc.y, sub2.curDim.width, sub2.curDim.height);
newX1 = cur1.x;
newY1 = cur1.y;
if (splitType == SPLIT_HOR)
{
if (spY == 0) return;
if (spY > 0)
{
if (enforceMinDim)
{
newHeight2 = Math.max(cur2.height - spY, min2.height);
}
else
{
newHeight2 = Math.max(cur2.height - spY, 2*iBdrSize);
}
newHeight1 = cur1.height + cur2.height - newHeight2;
}
else
{
if (enforceMinDim)
{
newHeight1 = Math.max(cur1.height + spY, min1.height);
}
else
{
newHeight1 = Math.max(cur1.height + spY, 2*iBdrSize);
}
newHeight2 = cur2.height + cur1.height - newHeight1;
}
newWidth1 = cur1.width;
newWidth2 = cur2.width;
newX2 = cur2.x;
newY2 = cur2.y - cur1.height + newHeight1;
sub1.reshapeHeight(newY1, newHeight1);
sub2.reshapeHeight(newY2, newHeight2);
}
if (splitType == SPLIT_VER)
{
if (spX == 0) return;
if (spX > 0)
{
if (enforceMinDim)
{
newWidth2 = Math.max(cur2.width - spX, min2.width);
}
else
{
newWidth2 = Math.max(cur2.width - spX, 2*iBdrSize);
}
newWidth1 = cur1.width + cur2.width - newWidth2;
}
else
{
if (enforceMinDim)
{
newWidth1 = Math.max(cur1.width + spX, min1.width);
}
else
{
newWidth1 = Math.max(cur1.width + spX, 1*iBdrSize);
}
newWidth2 = cur2.width + cur1.width - newWidth1;
}
newHeight1 = cur1.height;
newHeight2 = cur2.height;
newY2 = cur2.y;
newX2 = cur2.x - cur1.width + newWidth1;
sub1.reshapeWidth(newX1, newWidth1);
sub2.reshapeWidth(newX2, newWidth2);
}
sub1.invalidate();
sub1.validate();
sub1.repaint();
sub2.invalidate();
sub2.validate();
sub2.repaint();
}
}
/**
* This standard routine is called by the Java AWT (repaint()) to handle
* the repainting of this component on the screen.
* @param g the Graphics object
*/
public void update(Graphics g)
{
paint(g);
}
private void draw3DBdr(Graphics g, int x, int y, int width, int height, int bdrSize, Color c, boolean raised)
{
int i;
if (bdrSize < 0)
return;
g.setColor(use3dBdr ? c : c.darker());
for (i = 0; i < bdrSize; i++)
{
if (use3dBdr) g.draw3DRect(x + i, y + i, width - 2*i, height - 2*i, raised);
else g.drawRect(x + i, y + i, width - 2*i, height - 2*i);
}
}
/**
* This standard routine is called by the Java AWT to handle painting this
* component. It paints this component using the given graphics context.
* Overriding this method is not advised.
* @param g the graphics context used for painting
*/
public void paint(Graphics g)
{
Color bgColor;
Color fgColor;
if (dimChanged)
placeComponents();
Dimension d = size();
bgColor = getBackground();
g.setColor(bgColor);
if (isOuter)
{
g.setColor(gapColor);
g.fillRect(0, 0, d.width, d.height);
draw3DBdr(g, 0, 0, d.width-1, d.height-1, oBdrSize, gapColor, true);
}
else if (splitType == SPLIT_NONE)
{
g.setColor(bgColor);
g.fillRect(0, 0, d.width, d.height );
draw3DBdr(g, 0, 0, d.width-1, d.height-1, iBdrSize, gapColor, false);
}
else
{
g.setColor(gapColor);
g.fillRect(0, 0, d.width, d.height );
}
super.paint(g);
}
/**
* Gets the component in a SplitterPanel.
* @return the component in the SplitterPanel
*/
public Component getComponent()
{
if (isOuter)
return innerSP.getComponent();
return spComponent;
}
/**
* This standard Container routine gets an array of components in this
* Container. It is NOT supported.
* @return null
*/
public Component[] getComponents()
{
return null;
}
private void propagateChanges()
{
if (innerSP != null)
{
propagateChangesSP(innerSP);
innerSP.propagateChanges();
}
if (sub1 != null)
{
propagateChangesSP(sub1);
sub1.propagateChanges();
}
if (sub2 != null)
{
propagateChangesSP(sub2);
sub2.propagateChanges();
}
}
private void propagateChangesSP(SplitterPanel spTarget)
{
if ((spTarget.oGapWidth != this.oGapWidth) ||
(spTarget.iGapWidth != this.iGapWidth) ||
(spTarget.oGapHeight != this.oGapWidth) ||
(spTarget.iGapHeight != this.iGapHeight) ||
(spTarget.iBdrSize != this.iBdrSize) ||
(spTarget.oBdrSize != this.oBdrSize) ||
(spTarget.use3dBdr != this.use3dBdr))
{
this.dimChanged = true;
spTarget.dimChanged = true;
}
spTarget.oGapWidth = this.oGapWidth;
spTarget.iGapWidth = this.iGapWidth;
spTarget.oGapHeight = this.oGapWidth;
spTarget.iGapHeight = this.iGapHeight;
spTarget.iBdrSize = this.iBdrSize;
spTarget.oBdrSize = this.oBdrSize;
spTarget.gapColor = this.gapColor;
spTarget.enforceMinDim = this.enforceMinDim;
spTarget.moveSplitCursor = this.moveSplitCursor;
spTarget.propResize = this.propResize;
spTarget.use3dBdr = this.use3dBdr;
}
private Dimension maxDimOf(Dimension dim1, Dimension dim2)
{
return new Dimension(Math.max(dim1.width, dim2.width), Math.max(dim1.height, dim2.height));
}
/**
* Returns the recommended dimensions to properly display this component.
*/
public Dimension preferredSize()
{
Dimension theDim;
if (isOuter)
{
theDim = new Dimension(innerSP.preferredSize());
theDim.width += 2*(oGapWidth + oBdrSize);
theDim.height += 2*(oGapHeight + oBdrSize);
}
else
{
if (splitType == SPLIT_NONE)
{
if (spComponent == null)
{
theDim = new Dimension(0, 0);
}
else
{
theDim = new Dimension(spComponent.preferredSize());
}
theDim.width += 2*(iBdrSize);
theDim.height += 2*(iBdrSize);
}
else
{
Dimension theDim1;
Dimension theDim2;
theDim1 = sub1.preferredSize();
theDim2 = sub2.preferredSize();
if (splitType == SPLIT_HOR)
{
theDim = new Dimension(theDim1.width, theDim1.height + theDim2.height);
theDim.height += iGapHeight;
}
else
{
theDim = new Dimension(theDim1.width + theDim1.width, theDim1.height);
theDim.width += iGapWidth;
}
}
}
return maxDimOf(prefDim, theDim);
}
/**
* Returns the minimum dimensions to properly display this component.
*/
public Dimension minimumSize()
{
Dimension theDim;
if (isOuter)
{
theDim = new Dimension(innerSP.minimumSize());
theDim.width += 2*(oGapWidth + oBdrSize);
theDim.height += 2*(oGapHeight + oBdrSize);
}
else
{
if (splitType == SPLIT_NONE)
{
if (spComponent == null)
{
theDim = new Dimension(0, 0);
}
else
{
theDim = new Dimension(spComponent.minimumSize());
}
theDim.width += 2*(iBdrSize);
theDim.height += 2*(iBdrSize);
}
else
{
Dimension theDim1;
Dimension theDim2;
theDim1 = sub1.minimumSize();
theDim2 = sub2.minimumSize();
if (splitType == SPLIT_HOR)
{
theDim = new Dimension(theDim1.width, theDim1.height + theDim2.height);
theDim.height += iGapHeight;
}
else
{
theDim = new Dimension(theDim1.width + theDim1.width, theDim1.height);
theDim.width += iGapWidth;
}
}
}
return maxDimOf(minDim, theDim);
}
/**
* Sets the preferred size of a SplitterPanel. This will
* override the calculated size.
* @param theDim the dimension of the preferred size
*/
public void setPreferredSize(Dimension theDim)
{
prefDim.width = theDim.width;
prefDim.height = theDim.height;
//Must ensure that preferred size is as big as minimum size
prefDim = maxDimOf(theDim, prefDim);
dimChanged = true;
}
/**
* Sets the minimum size of a SplitterPanel. This will override
* the calculated size.
* @param theDim the dimension of the minimum size
*/
public void setMinimumSize(Dimension theDim)
{
minDim.width = theDim.width;
minDim.height = theDim.height;
prefDim = maxDimOf(theDim, prefDim);
dimChanged = true;
}
/**
* This standard Java AWT routine is called to move and/or resize this component.
* @param x the new position, x coordinate
* @param y the new position, y coordinate
* @param width the new width
* @param height the new height
* @see #reshapeHeight
* @see #reshapeWidth
*/
public synchronized void reshape (int x, int y, int width, int height) {
super.reshape(x, y, width, height);
if ((curDim.width != width) || (curDim.height != height) ||
(curLoc.x != x) || (curLoc.y != y))
{
dimChanged = true;
curDim.width = width;
curDim.height = height;
curLoc.x = x;
curLoc.y = y;
}
}
/**
* Reshape width only to prevent height adjustments when layout is computed.
* @param x new x position
* @param width new width
* @see #reshapeHeight
* @see #reshape
*/
public synchronized void reshapeWidth(int x, int width) {
Rectangle rect = new Rectangle(curLoc.x, curLoc.y, curDim.width, curDim.height);
if (curDim.width != width)
{
dimChanged = true;
widthOnly = true;
curDim.width = width;
}
reshape(x, rect.y, width, rect.height);
}
/**
* Reshape height only to prevent width adjustments when layout is computed.
* @param y new y position
* @param height new height
* @see #reshapeWidth
* @see #reshape
*/
public synchronized void reshapeHeight(int y, int height) {
Rectangle rect = new Rectangle(curLoc.x, curLoc.y, curDim.width, curDim.height);
if (curDim.height != height)
{
dimChanged = true;
heightOnly = true;
curDim.height = height;
}
reshape(rect.x, y, rect.width, height);
}
/**
* Add a single component to the SplitterPanel.
* This method supports addition of one component at a time.
* If there is already a component present, it is replaced
* by the given component.
* @param comp the component to add
* @param pos the position
* @return the added component, if successful
* @see #remove
**/
public synchronized Component add(Component comp, int pos)
{
if (comp == null)
return null;
if (isOuter)
return innerSP.add(comp, pos);
if (spComponent != null)
super.remove(spComponent);
spComponent = super.add(comp, -1);
dimChanged=true;
return spComponent;
}
/**
* Remove a component from the SplitterPanel.
* @param comp the component to remove
* @see #add
*/
public synchronized void remove(Component comp)
{
if (isOuter)
{
innerSP.remove(comp);
}
else
{
if (comp == spComponent)
{
spComponent = null;
super.remove(comp);
}
else
{
if (sub1 != null)
sub1.remove(comp);
if (sub2 != null)
sub2.remove(comp);
}
}
}
/**
* Remove all components from the SplitterPanel.
* Split panes are preserved.
*/
public synchronized void removeAll()
{
if (isOuter)
{
innerSP.removeAll();
}
else
{
super.removeAll();
if (sub1 != null)
{
super.add(sub1, -1);
sub1.removeAll();
}
if (sub2 != null)
{
super.add(sub2, -1);
sub1.removeAll();
}
}
spComponent = null;
}
/**
* Split a SplitterPanel into two panes. This creates
* two new SplitterPanels which are added to the original
* SplitterPanel and are located inside according to the
* split type.
* @param splitType SPLIT_VER or SPLIT_HOR to request
* vertical or horizontal splits
* @return the first new SplitterPanel if successful
* @see #SPLIT_VER
* @see #SPLIT_HOR
*/
public SplitterPanel split(int splitType)
{
return split(splitType, null, null);
}
/**
* Split a SplitterPanel into two panes and add two
* components.
* @param splitType SPLIT_VER or SPLIT_HOR to request
* vertical or horizontal splits
* @param theComp1 the first component (left or top)
* @param theComp2 the second component (left or top)
* @return the first new SplitterPanel if successful
* @see #SPLIT_VER
* @see #SPLIT_HOR
*/
public synchronized SplitterPanel split(int splitType, Component theComp1, Component theComp2)
{
if (isOuter)
return(this.innerSP.split(splitType, theComp1, theComp2));
if (this.splitType != SPLIT_NONE)
return null;
if ((splitType != SPLIT_VER) && (splitType != SPLIT_HOR))
return null;
sub1 = new SplitterPanel(false);
if (sub1 == null)
return null;
sub2 = new SplitterPanel(false);
if (sub2 == null)
{
super.remove(sub1);
return null;
}
this.splitType = splitType;
sub1.outerSP = this.outerSP;
sub2.outerSP = this.outerSP;
super.add(sub1, -1);
super.add(sub2, -1);
if (theComp1 != null)
sub1.add(theComp1);
if (theComp2 != null)
sub2.add(theComp2);
return sub1;
}
/**
* Layout managers are not used with this panel. Overridden to be empty.
*/
public void setLayout(LayoutManager mgr)
{
}
private Frame findFrame()
{
Class theFrameClass;
Container theContainer = this;
Class theClass = this.getClass();
try
{
theFrameClass = Class.forName("java.awt.Frame"); //(new Frame()).getClass();
}
catch (ClassNotFoundException e)
{
return null;
}
while (theContainer != null)
{
theContainer = theContainer.getParent();
theClass = theContainer.getClass();
while ((theClass != theFrameClass) && (theClass != null))
{
theClass = theClass.getSuperclass();
}
if (theClass == theFrameClass)
return (Frame)theContainer;
}
return null;
}
private void resetCursor()
{
Frame theFrame;
if (outerSP.cursorChanged == false) return;
theFrame = findFrame();
if (theFrame == null)
return;
try
{
theFrame.setCursor(theFrame.DEFAULT_CURSOR);
outerSP.cursorChanged = false;
cursorCh = null;
}
catch(IllegalArgumentException e)
{
return;
}
}
private int setCursor(int cursorID)
{
Frame theFrame;
if (outerSP.cursorChanged)
return cursorID;
if (!isOuter)
return outerSP.setCursor(cursorID);
theFrame = findFrame();
if (theFrame == null)
return Integer.MIN_VALUE;
try
{
theFrame.setCursor(cursorID);
cursorChanged = true;
}
catch(IllegalArgumentException e)
{
return Integer.MIN_VALUE;
}
return cursorID;
}
private boolean inComponent(Component comp, int x, int y)
{
//Point compLoc = comp.location();
Rectangle r = comp.bounds();
r.grow(-iBdrSize-1, -iBdrSize-1);
//return comp.inside(x - compLoc.x, y - compLoc.y);
return ((x-r.x >= 0) && (x-r.x <= r.width) && (y-r.y >= 0) && (y-r.y <= r.height));
}
private boolean inGap(Event evt)
{
return inGap(evt.x, evt.y);
}
private boolean inGap(int evtX, int evtY)
{
if (inGap1(evtX, evtY))
return true;
if (isOuter)
{
return innerSP.inGap(evtX - innerSP.curLoc.x, evtY - innerSP.curLoc.y);
}
else if ((splitType == SPLIT_HOR) || (splitType == SPLIT_VER))
{
return (sub1.inGap(evtX - sub1.curLoc.x, evtY - sub1.curLoc.y) ||
sub2.inGap(evtX - sub2.curLoc.x, evtY - sub2.curLoc.y));
}
return false;
}
private boolean inGap1(int evtX, int evtY)
{
if (!inside(evtX, evtY))
return false;
if (isOuter)
{
return false;
}
if (inOutBoarder(evtX, evtY)) {
return false;
}
else if ((splitType == SPLIT_HOR) || (splitType == SPLIT_VER))
{
if (inComponent(sub1, evtX, evtY) || (inComponent(sub2, evtX, evtY)))
{
return false;
}
else
{
if (inComponent(this, evtX, evtY)) cursorCh = this;
return true;
}
}
else
{
return false;
}
}
private boolean inOutBoarder(int evtX, int evtY) {
Component sp = this;
Rectangle r = sp.bounds();
int x = r.x;
int y = r.y;
while(sp != outerSP) {
sp = sp.getParent();
r = sp.bounds();
x += r.x;
y += r.y;
}
r.grow(-(outerSP.iBdrSize + outerSP.oBdrSize + outerSP.oGapWidth/2+4), -(outerSP.iBdrSize + outerSP.oBdrSize + outerSP.oGapHeight/2+4));
r.move(r.x + outerSP.oGapWidth/2, r.y + outerSP.oGapHeight/2);
return !r.inside(x + evtX, y + evtY);
}
/**
* Override this method if you want a behavior when events
* occur in the outer gap. Be sure to call super.oGapThis
* if you do override this method. Note that it is recommended
* that you place a SplitterPanel inside of a frame and set
* outer border and gap sizes to zero if you want the end user
* to be able to resize.
* @param evt the event that occurred in the outer gap around
* a SplitterPanel
*/
public boolean oGapThis(Event evt)
{
if (!doMoveSplit && !inGap(evt))
resetCursor();
return true;
}
/**
* The standard object string generator.
* @return a meaningful string about this object
*/
public String toString()
{
String typeString = "Bad Panel ";
Container theParent;
String theParentString;
if (isOuter)
{
typeString = "Outer ";
}
else
{
if (splitType == SPLIT_NONE)
{
typeString = "Unsplit ";
}
if (splitType == SPLIT_VER)
{
typeString = "Vertically split ";
}
if (splitType == SPLIT_HOR)
{
typeString = "Horizontally split ";
}
}
theParent = getParent();
if (theParent == null)
theParentString = " with no parent (not added)";
else
theParentString = " with parent @" + theParent.hashCode();
return typeString + super.toString() + ": @" + hashCode() + theParentString;
}
}